package org.lep.leetcode.clonegraph;

import java.lang.reflect.UndeclaredThrowableException;
import java.util.*;

/**
 * Source : https://oj.leetcode.com/problems/clone-graph/
 *
 * Created by lverpeng on 2017/8/29.
 *
 *
 * Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.
 *
 * OJ's undirected graph serialization:
 *
 * Nodes are labeled uniquely.
 *
 * We use # as a separator for each node, and , as a separator for node label and each neighbor of the node.
 *
 * As an example, consider the serialized graph {0,1,2#1,2#2,2}.
 *
 * The graph has a total of three nodes, and therefore contains three parts as separated by #.
 *
 * First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
 * Second node is labeled as 1. Connect node 1 to node 2.
 * Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle.
 *
 * Visually, the graph looks like the following:
 *
 *        1
 *       / \
 *      /   \
 *     0 --- 2
 *          / \
 *          \_/
 *
 */
public class CloneGraph {


    /**
     * 克隆无向图
     *
     * 使用BFS或者DFS
     *
     * HashMap用来记录已经被克隆过的node
     * key：被克隆过的node
     * value：由key克隆出来的node
     *
     * @param node
     * @return
     */
    public UndirectedGraphNode clone (UndirectedGraphNode node) {
        if(node == null) {
            return null;
        }
        Queue<UndirectedGraphNode> queue = new ArrayDeque<UndirectedGraphNode>();
        Map<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<UndirectedGraphNode, UndirectedGraphNode>();
        map.put(node, new UndirectedGraphNode(node.label));
        queue.offer(node);
        while (queue.size() > 0) {
            UndirectedGraphNode cur = queue.poll();
            UndirectedGraphNode cloneNode = map.get(cur);
            if (cloneNode.neighbors == null) {
                cloneNode.neighbors = new ArrayList<UndirectedGraphNode>();
            }
            if (cur.neighbors == null) {
                continue;
            }
            for (UndirectedGraphNode neighbor : cur.neighbors) {
                if (map.containsKey(neighbor)) {
                    cloneNode.neighbors.add(map.get(neighbor));
                } else {
                    UndirectedGraphNode temp = new UndirectedGraphNode(neighbor.label);
                    cloneNode.neighbors.add(temp);
                    map.put(neighbor, temp);
                    queue.offer(neighbor);
                }
            }
        }

        return map.get(node);
    }

    /**
     *
     * 根据字符串创建图的方法不对。。。待完善
     * @param graphSrt
     * @return
     */
    public static UndirectedGraphNode createGraph (String graphSrt) {
        if (graphSrt == null || graphSrt.length() == 0) {
            return null;
        }
        Map<Integer, UndirectedGraphNode> map = new HashMap<Integer, UndirectedGraphNode>();
        String[] nodeStrs = graphSrt.split("#");
        UndirectedGraphNode first = null;
        for (int i = 0; i < nodeStrs.length; i++) {
            String[] nodeArr = nodeStrs[i].split(",");
            UndirectedGraphNode node = null;
            for (int j = 0; j < nodeArr.length; j++) {
                Integer label = Integer.parseInt(nodeArr[j]);

                if (j == 0) {
                    if (map.containsKey(label)) {
                        node = map.get(label);
                    } else {
                        node = new UndirectedGraphNode(label);
                        map.put(label, node);
                    }
                    if (first == null) {
                        first = node;
                    }
                } else {
                    UndirectedGraphNode neighbor = null;
                    if (map.containsKey(label)) {
                        neighbor = map.get(label);
                    } else {
                        neighbor = new UndirectedGraphNode(label);
                        map.put(label, neighbor);
                    }
                    if (node.neighbors == null) {
                        node.neighbors =  new ArrayList<UndirectedGraphNode>();
                    }
                    if (neighbor.label == node.label) {
                        neighbor = new UndirectedGraphNode(label);
                    }
                    node.neighbors.add(neighbor);
                }
            }
        }
        return first;
    }

    public static void print (UndirectedGraphNode node) {
        if (node == null) {
            return;
        }
        StringBuffer buffer = new StringBuffer();
        Queue<UndirectedGraphNode> queue = new ArrayDeque<UndirectedGraphNode>();
        queue.offer(node);
        while (queue.size() > 0) {
            UndirectedGraphNode cur = queue.poll();
            buffer.append(cur.label);
            for (UndirectedGraphNode neighbor : cur.neighbors) {
                buffer.append(",");
                buffer.append(neighbor.label);
                queue.offer(neighbor);
            }
            buffer.append("#");
        }
        if (buffer.length() > 0) {
            buffer.deleteCharAt(buffer.length()-1);
        }
        System.out.println(buffer);
    }

    private static class UndirectedGraphNode {
        int label;
        List<UndirectedGraphNode> neighbors;

        public UndirectedGraphNode(int label) {
            this.label = label;
        }
    }

    public static void main(String[] args) {
        CloneGraph cloneGraph = new CloneGraph();
        String graphStr = "0,1,2#1,2#2,2";
        print(cloneGraph.clone(createGraph(graphStr)));
    }
}
